#include <adapter/frontend.h>

#include <demod/dib8000.h>
#include <tuner/dib0090.h>
#include "dibx09x_common.h"
#include <sip/dib8090.h>

extern int dib0090_gain_control(struct dibFrontend *fe, struct dibChannel *ch);

struct dib8090_state {
    struct dibSIPInfo info;
    struct dib0090_config dib0090_cfg;
    const struct dib8090_config *cfg;
    struct dib8000_config *dib8000_cfg;

    int (*update_lna)   (struct dibFrontend *, uint16_t);
};

static const struct dibDebugObject dib8090m_dbg = {
    DEBUG_SIP,
    "DiB8090"
};

static const struct dibx000_agc_config dib8090_agc_config[2] = {
    {
        BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
        (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup

        787,// inv_gain = 1/ 90.4dB // no boost, lower gain due to ramp quantification
        10,  // time_stabiliz

        0,  // alpha_level
        118,  // thlock

        0,     // wbd_inv
        3530,  // wbd_ref
        1,     // wbd_sel
        5,     // wbd_alpha

#if 0
        27265,  // agc1_max     = 0.415 == 27229 to 27265 = 28 steps
        27265,  // agc1_min
#endif

        65535,  // agc1_max
        0,      // agc1_min

        32767,  // agc2_max
        0,      // agc2_min

        0,      // agc1_pt1
        32,     // agc1_pt2
        114,    // agc1_pt3  // 40.4dB
        143,    // agc1_slope1
        144,    // agc1_slope2
        114,    // agc2_pt1
        227,    // agc2_pt2
        116,    // agc2_slope1
        117,    // agc2_slope2

        28,  // alpha_mant // 5Hz with 90.2dB
        26,  // alpha_exp
        31,  // beta_mant
        51,  // beta_exp

        0,  // perform_agc_softsplit
    },
    {
        BAND_CBAND,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
        (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup

        787,// inv_gain = 1/ 90.4dB // no boost, lower gain due to ramp quantification
        10,  // time_stabiliz

        0,  // alpha_level
        118,  // thlock

        0,     // wbd_inv
        3530,  // wbd_ref
        1,     // wbd_sel
        5,     // wbd_alpha

#if 0
        27265,  // agc1_max     = 0.415 == 27229 to 27265 = 28 steps
        27265,  // agc1_min
#endif

        0,  // agc1_max
        0,  // agc1_min

        32767,  // agc2_max
        0,      // agc2_min

        0,      // agc1_pt1
        32,     // agc1_pt2
        114,    // agc1_pt3  // 40.4dB
        143,    // agc1_slope1
        144,    // agc1_slope2
        114,    // agc2_pt1
        227,    // agc2_pt2
        116,    // agc2_slope1
        117,    // agc2_slope2

        28,  // alpha_mant // 5Hz with 90.2dB
        26,  // alpha_exp
        31,  // beta_mant
        51,  // beta_exp

        0,  // perform_agc_softsplit
    }
};

static const struct dibx000_agc_config dib8090_agc_config_hi_level[2] = {
    {
        BAND_UHF | BAND_VHF,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
        (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup

        787,// inv_gain = 1/ 90.4dB // no boost, lower gain due to ramp quantification
        10,  // time_stabiliz

        0,  // alpha_level
        118,  // thlock

        0,     // wbd_inv
        3530,  // wbd_ref
        1,     // wbd_sel
        5,     // wbd_alpha

#if 0
        27265,  // agc1_max     = 0.415 == 27229 to 27265 = 28 steps
        27265,  // agc1_min
#endif

        65535,  // agc1_max
        0,      // agc1_min

        32767,  // agc2_max
        0,      // agc2_min

        0,      // agc1_pt1
        32,     // agc1_pt2
        114,    // agc1_pt3  // 40.4dB
        143,    // agc1_slope1
        144,    // agc1_slope2
        114,    // agc2_pt1
        227,    // agc2_pt2
        116,    // agc2_slope1
        117,    // agc2_slope2

        28,  // alpha_mant // 5Hz with 90.2dB
        26,  // alpha_exp
        31,  // beta_mant
        51,  // beta_exp

        0,  // perform_agc_softsplit
    },
    {
        BAND_CBAND,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
        (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup

        787,// inv_gain = 1/ 90.4dB // no boost, lower gain due to ramp quantification
        10,  // time_stabiliz

        0,  // alpha_level
        118,  // thlock

        0,     // wbd_inv
        3530,  // wbd_ref
        1,     // wbd_sel
        5,     // wbd_alpha

#if 0
        27265,  // agc1_max     = 0.415 == 27229 to 27265 = 28 steps
        27265,  // agc1_min
#endif

        0,  // agc1_max
        0,  // agc1_min

        32767,  // agc2_max
        0,      // agc2_min

        0,      // agc1_pt1
        32,     // agc1_pt2
        114,    // agc1_pt3  // 40.4dB
        143,    // agc1_slope1
        144,    // agc1_slope2
        114,    // agc2_pt1
        227,    // agc2_pt2
        116,    // agc2_slope1
        117,    // agc2_slope2

        28,  // alpha_mant // 5Hz with 90.2dB
        26,  // alpha_exp
        31,  // beta_mant
        51,  // beta_exp

        0,  // perform_agc_softsplit
    }
};

static int dib8090_tuner_sleep(struct dibFrontend *fe, int onoff)
{
    dbgpl(&dib8090m_dbg, "sleep dib0090: %d", onoff);
    demod_set_gpio(fe, 0, 0, (uint8_t)onoff);
    return DIB_RETURN_SUCCESS;
}

static int dib8090_tuner_reset(struct dibFrontend *fe, int onoff)
{
    dbgpl(&dib8090m_dbg, "reset dib0090: %d", onoff);
    demod_set_gpio(fe, 5, 0, !onoff);
    return DIB_RETURN_SUCCESS;
}

#ifdef CONFIG_DIB0090_USE_PWM_AGC
static int dib8090_agc_control(struct dibFrontend *fe, uint8_t restart)
{
    dbgpl(&dib8090m_dbg, "AGC control callback: %d", restart);
    dib0090_dcc_freq(fe, restart);

    if(restart == 0) /* before AGC startup */
        dib0090_set_dc_servo(fe, 1);

    return DIB_RETURN_SUCCESS;
}
#endif

static int dib8090_update_lna(struct dibFrontend *fe, uint16_t agc_global)
{
    struct dib8090_state *state = fe->sip->priv;

    if (state->update_lna)
        state->update_lna(fe, agc_global);

    if (state->cfg->use_high_level_adjustment & 1) {
        dbgpl(&dib8090m_dbg, "dib8090_update_lna, agc_global=%d A/D power=%i", agc_global, dib8000_get_adc_power(fe, 0));

        // maximum gain and A/D power below -13dBVrms
        if ((agc_global == 0) && (dib8000_get_adc_power(fe, 0)> 52553))
            dib8000_set_agc1_min(fe, 0);
        else
            dib8000_set_agc1_min(fe, 32768);
    }

    return DIB_RETURN_SUCCESS;
}

static const struct dibx000_bandwidth_config dib8090_pll_config_30mhz[3] = {
{
    52500, 13125, // internal, sampling
    1, 7, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (599 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    20776863, // timf
    30000000, // xtal_hz
},
{
    57000, 14250, // internal, sampling
    5, 38, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (551 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    22325930, // timf
    30000000, // xtal_hz
},
{
    65625, 16406, // internal, sampling
    4, 35, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (479 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    22161408, // timf
    30000000, // xtal_hz
}};

static const struct dibx000_bandwidth_config dib8090_pll_config_15mhz[3] = {
{
    52500, 13125, // internal, sampling
    1, 14, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (599 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    20776863, // timf
    15000000, // xtal_hz
},
{
    56250, 14062, // internal, sampling
    1, 15, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (559 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    22623402, // timf
    15000000, // xtal_hz
},
{
    65625, 16406, // internal, sampling
    2, 35, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (479 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    221614408, // timf
    15000000, // xtal_hz
}};

static const struct dibx000_bandwidth_config dib8090_pll_config_12mhz[3] = {
{
    54000, 13500, // internal, sampling
    1, 18, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (599 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    20199727, // timf
    12000000, // xtal_hz
},
{
    57000, 14500, // internal, sampling
    1, 19, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (551 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    22325930, // timf
    12000000, // xtal_hz
},
{
    66000, 16500, // internal, sampling
    1, 22, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
    0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
    (3 << 14) | (1 << 12) | (476 << 0), // sad_cfg: refsel, sel, freq_15k
    (0 << 25) | 0, // ifreq = 0 MHz
    22035797, // timf
    12000000, // xtal_hz
}};

static int dib8090_get_adc_power(struct dibFrontend *fe)
{
    return dib8000_get_adc_power(fe, 1);
}

static const struct dib8000_config default_dib8000_config = {
    0, // output_mpeg2_in_188_bytes
    0, // hostbus_diversity
    dib8090_update_lna, // update_lna

    2, // agc_config_count
    dib8090_agc_config,
    NULL,

    DIB8000_GPIO_DEFAULT_DIRECTIONS, // gpio_dir
    DIB8000_GPIO_DEFAULT_VALUES,  // gpio_val
    DIB8000_GPIO_DEFAULT_PWM_POS, // gpio_pwm_pos
    0,

#ifdef CONFIG_DIB0090_USE_PWM_AGC
    dib8090_agc_control,
#else
    NULL,
#endif
};

static const struct dib0090_wbd_slope dib8090_wbd_table[] = {
    /* max freq ; cold slope ; cold offset ; warm slope ; warm offset ; wbd gain */
    { 120,     0, 500,  0,   500, 4 }, /* CBAND */ /* TODO */
    { 170,     0, 450,  0,   450, 4 }, /* CBAND */ /* TODO */
    { 380,    48, 373, 28,   259, 6 }, /* VHF */
    { 860,    34, 700, 36,   616, 6 }, /* high UHF */
    { 0xFFFF, 34, 700, 36,   616, 6 }, /* default */
};

static const struct dib0090_config default_dib0090_config = {
    { 0 },
    dib8090_tuner_reset,
    dib8090_tuner_sleep,
    0,0,0,0,0,192,
    0,0,
    1, // use PWM
    7,  // clkoutdrive
    0, 0,
    dib8090_wbd_table,
};


#ifndef CONFIG_SIP_DIB8090_AUTOMATIC_PLL_RATIO_COMPUTATION
struct dib8096_adc {
    uint32_t freq;
    uint32_t pll_div;
};

static struct dib8096_adc dib8096_adc_tab[] = {
    {174143, 20},
    {180143, 19},
    {184143, 18},
    {186143, 17},
    {190143, 20},
    {192143, 19},
    {196143, 18},
    {202143, 20},
    {208143, 19},
    {214143, 17},
    {220143, 20},
    {226143, 18},
    {232143, 20},

    {474143, 20},
    {480143, 18},
    {486143, 20},
    {492143, 17},
    {504143, 20},
    {510143, 18},
    {522143, 20},
    {528143, 17},
    {534143, 18},
    {540143, 17},
    {546143, 18},
    {552143, 20},
    {558143, 18},
    {564143, 20},
    {570143, 17},
    {576143, 18},
    {582143, 17},
    {588143, 18},
    {594143, 17},
    {600143, 18},
    {606143, 17},
    {612143, 20},
    {618143, 17},
    {624143, 20},
    {630143, 18},
    {636143, 20},
    {642143, 18},
    {648143, 17},
    {654143, 20},
    {660143, 17},
    {666143, 20},
    {672143, 17},
    {678143, 19},
    {684143, 20},
    {690143, 19},
    {702143, 20},
    {708143, 18},
    {714143, 20},
    {720143, 17},
    {726143, 18},
    {732143, 17},
    {738143, 18},
    {744143, 20},
    {750143, 18},
    {756143, 20},
    {762143, 18},
    {768143, 18},
    {774143, 20},
    {780143, 18},
    {786143, 17},
    {792143, 19},
    {798143, 17},
    {0xffffffff, 18},
};
#endif

static uint8_t dib8090_compute_pll_parameters(struct dibFrontend *fe, struct dibChannel *ch)
{
    uint8_t optimal_pll_ratio = 20;
#ifdef CONFIG_SIP_DIB8090_AUTOMATIC_PLL_RATIO_COMPUTATION
	uint32_t freq_adc, ratio, rest, max=0;
	uint8_t pll_ratio;
	for (pll_ratio=17; pll_ratio<=20; pll_ratio++) {
		freq_adc = 12*pll_ratio*(1<<8)/16;
		ratio = (ch->RF_kHz*(1<<8)/1000)/freq_adc;
		rest = (ch->RF_kHz*(1<<8)/1000)-ratio*freq_adc;

		if (rest>freq_adc/2)
			rest = freq_adc-rest;
	    dbgpl(&dib8090m_dbg, "PLL ratio=%i rest=%i", pll_ratio, rest);
		if ((rest>max) && (rest > 717)) {
			optimal_pll_ratio = pll_ratio;
			max = rest;
		}
	}
#else
    uint16_t freq_index = 0;

    while (dib8096_adc_tab[freq_index].freq < ch->RF_kHz)
        freq_index++;
    optimal_pll_ratio = dib8096_adc_tab[freq_index].pll_div;
#endif
	dbgpl(&dib8090m_dbg, "optimal PLL ratio=%i", optimal_pll_ratio);

    return optimal_pll_ratio;
}


static int dib8090_agc_startup(struct dibFrontend *fe, struct dibChannel *ch)
{
    uint8_t band = (uint8_t) channel_frequency_band(ch->RF_kHz);
    struct dib8090_state *state = fe->sip->priv;
    int ret;
    uint8_t pll_ratio;
    uint32_t timf;

    if (fe->tune_state == CT_AGC_START) {
        if (ch->bandwidth_kHz == 6000) {

            /** Use 6Mhz PLL config **/
            if (state->dib8000_cfg->plltable != NULL)
                state->dib8000_cfg->pll = &state->dib8000_cfg->plltable[0];
            /** Update PLL if needed ratio **/
            dib8000_update_pll(fe, state->dib8000_cfg->pll, ch->bandwidth_kHz, 0);

            /** Get optimize PLL ratio to remove spurious **/
            pll_ratio = dib8090_compute_pll_parameters(fe, ch);
            if (pll_ratio == 17)
                timf = 21387946;
            else if (pll_ratio == 18)
                timf = 20199727;
            else if (pll_ratio == 19)
                timf = 19136583;
            else
                timf = 18179756;

            /** Update ratio **/
            dib8000_update_pll(fe, state->dib8000_cfg->pll, ch->bandwidth_kHz, pll_ratio);
        }
        else if (ch->bandwidth_kHz == 7000) {
            /** Use 7Mhz PLL config **/
            if (state->dib8000_cfg->plltable != NULL)
                state->dib8000_cfg->pll = &state->dib8000_cfg->plltable[1];
            /** Update PLL if needed **/
            dib8000_update_pll(fe, state->dib8000_cfg->pll, ch->bandwidth_kHz, 0);
            timf = state->dib8000_cfg->pll->timf;
      }
		else if (ch->bandwidth_kHz == 8000) {
            /** Use 8Mhz PLL config **/
            if (state->dib8000_cfg->plltable != NULL)
                state->dib8000_cfg->pll = &state->dib8000_cfg->plltable[2];
            /** Update PLL if needed **/
            dib8000_update_pll(fe, state->dib8000_cfg->pll, ch->bandwidth_kHz, 0);
            timf = state->dib8000_cfg->pll->timf;
      }

		if (FE_DEMOD_FUNC_CHECK(fe, ctrl_timf))
			FE_DEMOD_FUNC_CALL(fe, ctrl_timf)(fe, DEMOD_TIMF_SET, timf);

        if (band != BAND_CBAND) {
            /* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */
            uint16_t target = (dib0090_get_wbd_target(fe, ch->RF_kHz) * 8 * 18 / 33 + 1) / 2;
            demod_set_wbd_ref(fe, target);
        } /* else-path is handled in the dib0090 */

    }

#ifdef CONFIG_DIB0090_USE_PWM_AGC
    if (band == BAND_CBAND) {
        if (fe->tune_state == CT_AGC_START) {
            dbgpl(&dib8090m_dbg, "tuning in CBAND - soft-AGC startup");
            /* TODO specific wbd target for dib0090 - needed for startup ? */
        }

        if (fe->tune_state < CT_AGC_STOP)
#endif
            ret = dib0090_gain_control(fe, ch);
#ifdef CONFIG_DIB0090_USE_PWM_AGC
        else
            ret = FE_CALLBACK_TIME_NEVER;

        if (fe->tune_state == CT_AGC_STOP) {
            dbgpl(&dib8090m_dbg, "switching to PWM AGC");
            dib0090_pwm_gain_reset(fe, ch);
            dib8000_pwm_agc_reset(fe, ch);
        }
    } else { /* for everything else than CBAND we are using standard AGC */
        if (fe->tune_state == CT_AGC_START)
            dib0090_pwm_gain_reset(fe, ch);
        ret = dib8000_agc_startup(fe, ch);
    }
#endif
    return ret;
}

static void dib8090_release(struct dibFrontend *fe)
{
    struct dib8090_state *state = fe->sip->priv;
    if (state->dib8000_cfg != NULL)
        MemFree(state->dib8000_cfg, sizeof(struct dib8000_config));
    MemFree(state,sizeof(struct dib8090_state));
}

int dib8090_set_wbd_table(struct dibFrontend *fe, const struct dib0090_wbd_slope *wbd)
{
	return dib0090_set_wbd_table(fe, wbd);
}

static const struct dibSIPInfo dib8090_info = {
    "DiBcom DiB8090MB",

    {
        dib8090_release
    }
};

struct dibFrontend * dib8090_sip_register(struct dibFrontend *fe, struct dibDataBusHost *host, uint8_t addr, const struct dib8090_config *cfg)
{
    struct dib8090_state *state;
    struct dib8000_config dib8000_cfg;

    state = MemAlloc(sizeof(struct dib8090_state));
    if (state == NULL)
        return NULL;
    DibZeroMemory(state, sizeof(struct dib8090_state));

    state->dib8000_cfg = MemAlloc(sizeof(struct dib8000_config));
    if (state->dib8000_cfg == NULL)
        goto error;
    DibZeroMemory(state->dib8000_cfg, sizeof(struct dib8000_config));

    frontend_register_sip(fe, &state->info, &dib8090_info, state);

    state->cfg = cfg;

    memcpy(&dib8000_cfg, &default_dib8000_config, sizeof(struct dib8000_config));
    memcpy(&state->dib0090_cfg, &default_dib0090_config, sizeof(struct dib0090_config));

    switch (cfg->clock_khz) {
    case 12000:
        dib8000_cfg.plltable = dib8090_pll_config_12mhz;
        dib8000_cfg.pll = &dib8000_cfg.plltable[0];
        memcpy(&state->dib0090_cfg.io, &dibx09x_io_12mhz_120, sizeof(dibx09x_io_12mhz_120));
        break;
    case 30000:
        dib8000_cfg.plltable = dib8090_pll_config_30mhz;
        dib8000_cfg.pll = &dib8000_cfg.plltable[0];
        memcpy(&state->dib0090_cfg.io, &dibx09x_io_30mhz_120, sizeof(dibx09x_io_30mhz_120));
        break;
    default:
        dbgpl(&dib8090m_dbg, "Error : %d is not a valid crystal frequency, please add the config for this frequency", cfg->clock_khz);
        goto error;
    }

    dib8000_cfg.output_mpeg2_in_188_bytes = cfg->output_mpeg2_in_188_bytes;
    dib8000_cfg.hostbus_diversity         = 1;
    dib8000_cfg.gpio_dir                  = cfg->gpio_dir;
    dib8000_cfg.gpio_val                  = cfg->gpio_val;
    dib8000_cfg.gpio_pwm_pos              = cfg->gpio_pwm_pos;
    dib8000_cfg.drives                    = cfg->dib8k_drives;
    dib8000_cfg.diversity_delay           = cfg->diversity_delay;
    dib8000_cfg.div_cfg                   = cfg->div_cfg;
    dib8000_cfg.refclksel                 = cfg->refclksel;
    dib8000_cfg.forward_erronous_mpeg_packet = cfg->forward_erronous_mpeg_packet;

    if (cfg->use_high_level_adjustment & 1)
        dib8000_cfg.agc = dib8090_agc_config_hi_level;

    memcpy(state->dib8000_cfg, &dib8000_cfg, sizeof(struct dib8000_config));

    state->update_lna                     = cfg->update_lna;


    state->dib0090_cfg.io.pll_bypass = 1;
    state->dib0090_cfg.freq_offset_khz_uhf = cfg->dib0090_freq_offset_khz_uhf;
    state->dib0090_cfg.freq_offset_khz_vhf = cfg->dib0090_freq_offset_khz_vhf;
    state->dib0090_cfg.clkouttobamse = cfg->clkouttobamse;
    state->dib0090_cfg.analog_output = 1;
    state->dib0090_cfg.clkoutdrive   = cfg->clkoutdrive;
    state->dib0090_cfg.fref_clock_ratio = cfg->fref_clock_ratio;
	if (cfg->dib0090_wbd_table != NULL)
		state->dib0090_cfg.wbd = cfg->dib0090_wbd_table;

    if (dib8000_register(fe, host, addr, &dib8000_cfg) == NULL)
        goto error;

    state->dib0090_cfg.get_adc_power = dib8090_get_adc_power;
    host = dib8000_get_i2c_master(fe, DIBX000_I2C_INTERFACE_TUNER, cfg->tuner_gated_i2c);
    if (dib0090_register(fe, host, &state->dib0090_cfg) == NULL)
        goto tuner_error;

    /* custom AGC start for DiB8090 */
    fe->demod_info->ops.agc_startup = dib8090_agc_startup;

    return fe;

tuner_error:
    frontend_unregister_demod(fe);
error:
    MemFree(state, sizeof(struct dib8090_state));

    return NULL;
}

